package com.bc.bcapi.service;

import com.bc.bcapi.dao.T_Erc_contractMapper;
import com.bc.bcapi.dao.T_Eth_blockchain_watcherMapper;
import com.bc.bcapi.utils.web3j.ERC20TransactionModel;
import com.bc.bcapi.utils.web3j.TransModel;
import com.bc.bcapi.utils.web3j.Util;
import com.bc.bccore.redis.RedisService;
import com.bc.bccore.web3j.WalletService;
import com.bc.bcmodel.consts.RedissCacheKey;
import com.bc.bcmodel.domain.T_Erc_contractModel;
import com.bc.bcmodel.domain.T_Eth_blockchain_watcherModel;
import com.bc.commoncore.util.SpringBeanHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.web3j.protocol.core.methods.response.Transaction;
import org.web3j.protocol.core.methods.response.TransactionReceipt;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@Service
public class BlockChainListenerService {

    private final static Logger log = LoggerFactory.getLogger(BlockChainListenerService.class);

    @Resource
    RedisService redisService;
    @Resource
    T_Eth_blockchain_watcherMapper t_eth_blockchain_watcherMapper;

    private static final BigInteger BLOCKCONFIRM = new BigInteger("30");

    private T_Eth_blockchain_watcherModel t_eth_blockchain_watcherModel;
    private HashMap<String, T_Erc_contractModel> contractModels;
    private List<String> txHashs = new ArrayList<>();

    private BigInteger getCurrentBlockChainHeight() throws IOException {
        return WalletService.getInstance().blockNumber();
    }

    private void getBlock() {
        txHashs = new ArrayList<>();
        t_eth_blockchain_watcherModel = new T_Eth_blockchain_watcherModel();
        T_Eth_blockchain_watcherModel lb = t_eth_blockchain_watcherMapper.getLastScanedBlock();
        if (lb.getStatus() == 1) {
            t_eth_blockchain_watcherModel.setBlock_height(lb.getBlock_height().add(new BigInteger("1")));
            t_eth_blockchain_watcherMapper.insert(t_eth_blockchain_watcherModel);
        } else {
            t_eth_blockchain_watcherModel = lb;
        }
    }

    private void loadContracts() {
        contractModels = new HashMap<>();
        T_Erc_contractMapper t_erc_contractMapper = SpringBeanHelper.getCustomBeanByType(T_Erc_contractMapper.class);
        List<T_Erc_contractModel> contracts = t_erc_contractMapper.selectAllContract();
        for (T_Erc_contractModel contract: contracts) {
            String contract_address = contract.getContract_address();
            contractModels.put(Util.outAddressFormat(contract_address), contract);
        }
    }

    public List<ERC20TransactionModel> scanBlock() throws IOException {
        getBlock();
        loadContracts();
        BigInteger currentHeight = getCurrentBlockChainHeight();
        BigInteger block_height = t_eth_blockchain_watcherModel.getBlock_height();
        System.out.println("currentHeight:" + currentHeight + " processingHeight:" + block_height);

        List<ERC20TransactionModel> transactionModels = new ArrayList<>();

        if (block_height.add(BLOCKCONFIRM).compareTo(currentHeight) < 0) {
            //有30个确认，有效block
            //取txhash
            List<String> txhashs = WalletService.getInstance().getBlockTransactions(block_height);
            String key = RedissCacheKey.Block_Chain_Txhash + block_height;
            for (String txhash : txhashs) {
                log.info("process txhash:" + txhash);
                String txhashStatus = redisService.hget(key, txhash);
                if (txhashStatus == null || txhashStatus.equals("0")) {
                    addTxHash(txhash);
                    Transaction transaction = WalletService.getInstance().getTransactionByHash(txhash);
                    TransactionReceipt transactionReceipt = WalletService.getInstance().getTransactionReceipt(txhash);
                    ERC20TransactionModel transactionModel = new ERC20TransactionModel(transaction, transactionReceipt);

                    for (TransModel transModel : transactionModel.getTransModels()) {
                        String address = transModel.getContract_address();
                        if (contractModels.containsKey(address)) {
                            transModel.setContractModel(contractModels.get(address));
                        }
                    }
                    transactionModels.add(transactionModel);
                }
            }
        } else return null;
        return transactionModels;
    }

    public void closeTxHash(String txhash) {
        BigInteger block_height = t_eth_blockchain_watcherModel.getBlock_height();
        String key = RedissCacheKey.Block_Chain_Txhash + block_height;
        redisService.hset(key, txhash, "1");
    }

    public void closeBlock() {
        T_Eth_blockchain_watcherModel updateModel = new T_Eth_blockchain_watcherModel();
        updateModel.setId(t_eth_blockchain_watcherModel.getId());
        updateModel.setStatus(1);
        t_eth_blockchain_watcherMapper.update(updateModel);
    }

    public List<String> getTxHashs() {
        return txHashs;
    }

    private void addTxHash(String txHash) {
        this.txHashs.add(txHash);
    }
}
